
package io.gitee.h25094152.crypto.rsa;


import io.gitee.h25094152.date.DateUtil;
import com.alibaba.fastjson.JSON;
import org.apache.commons.codec.binary.Base64;


import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author wh
 */
public class RSAUtils {

    public static final String CHARSET = "UTF-8";
    public static final String RSA_ALGORITHM = "RSA"; // ALGORITHM ['ælgərɪð(ə)m] 算法的意思

    public static Map<String, String> createKeys(int keySize) {
        // 为RSA算法创建一个KeyPairGenerator对象
        KeyPairGenerator kpg;
        try {
            kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM);
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException("No such algorithm-->[" + RSA_ALGORITHM + "]");
        }

        // 初始化KeyPairGenerator对象,密钥长度
        kpg.initialize(keySize);
        // 生成密匙对
        KeyPair keyPair = kpg.generateKeyPair();
        // 得到公钥
        Key publicKey = keyPair.getPublic();
        String publicKeyStr = Base64.encodeBase64URLSafeString(publicKey.getEncoded());
        // 得到私钥
        Key privateKey = keyPair.getPrivate();
        String privateKeyStr = Base64.encodeBase64URLSafeString(privateKey.getEncoded());
        // map装载公钥和私钥
        Map<String, String> keyPairMap = new HashMap<String, String>();
        keyPairMap.put("publicKey", publicKeyStr);
        keyPairMap.put("privateKey", privateKeyStr);
        // 返回map
        return keyPairMap;
    }

    /**
     * 得到公钥
     *
     * @param publicKey 密钥字符串（经过base64编码）
     * @throws Exception
     */
    public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        // 通过X509编码的Key指令获得公钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));

        System.out.println("x509KeySpec = " + x509KeySpec);

        RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
        return key;
    }

    /**
     * 得到私钥
     *
     * @param privateKey 密钥字符串（经过base64编码）
     * @throws Exception
     */
    public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        // 通过PKCS#8编码的Key指令获得私钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
        RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
        return key;
    }

    /**
     * 公钥加密
     *
     * @param data
     * @param publicKey
     * @return
     */
//    public static String publicEncrypt(String data, RSAPublicKey publicKey) {
//        try {
//            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
//            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
//            return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), publicKey.getModulus().bitLength()));
//        } catch (Exception e) {
//            throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
//        }
//    }
    public static String publicEncrypt(String data, RSAPublicKey publicKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return Base64.encodeBase64String(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), publicKey.getModulus().bitLength()));
        } catch (Exception e) {
            throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
        }
    }

    /**
     * 私钥解密
     *
     * @param data
     * @param privateKey
     * @return
     */

    public static String privateDecrypt(String data, RSAPrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), privateKey.getModulus().bitLength()), CHARSET);
        } catch (Exception e) {
            throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
        }
    }

    /**
     * 私钥加密
     *
     * @param data
     * @param privateKey
     * @return
     */

    public static String privateEncrypt(String data, RSAPrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            //每个Cipher初始化方法使用一个模式参数opmod，并用此模式初始化Cipher对象。此外还有其他参数，包括密钥key、包含密钥的证书certificate、算法参数params和随机源random。
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), privateKey.getModulus().bitLength()));
        } catch (Exception e) {
            throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
        }
    }

    /**
     * 公钥解密
     *
     * @param data
     * @param publicKey
     * @return
     */

    public static String publicDecrypt(String data, RSAPublicKey publicKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
            return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), publicKey.getModulus().bitLength()), CHARSET);
        } catch (Exception e) {
            throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
        }
    }

    //rsa切割解码  , ENCRYPT_MODE,加密数据   ,DECRYPT_MODE,解密数据
    private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize) {
        int maxBlock = 0;  //最大块
        if (opmode == Cipher.DECRYPT_MODE) {
            maxBlock = keySize / 8;
        } else {
            maxBlock = keySize / 8 - 11;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] buff;
        int i = 0;
        try {
            while (datas.length > offSet) {
                if (datas.length - offSet > maxBlock) {
                    //可以调用以下的doFinal（）方法完成加密或解密数据：
                    buff = cipher.doFinal(datas, offSet, maxBlock);
                } else {
                    buff = cipher.doFinal(datas, offSet, datas.length - offSet);
                }
                out.write(buff, 0, buff.length);
                i++;
                offSet = i * maxBlock;
            }
        } catch (Exception e) {
            throw new RuntimeException("加解密阀值为[" + maxBlock + "]的数据时发生异常", e);
        }
        byte[] resultDatas = out.toByteArray();
        IOUtils.closeQuietly(out);
        return resultDatas;
    }


    private static String xmlPrivateKey = "<RSAKeyValue><Modulus>rkidLWsQ+lqFUysLfgkYo5Trv7Rv7M4IrHMaHdpbWeLtpDcVaVUOr2Z6ZC4qjlbsGCORNYvAK3rLPDj7oJWk/9wSx4713Pr0nWMyu6kxVOYrIu7vpVZ/1Z/1gwYzLemDq/hbWHWKBA+jKwsR1WjjTRibARWww1RuOiyFnZ0Ge90=</Modulus><Exponent>AQAB</Exponent><P>zcnsVwjU0gVyaA1/AW7qYaZy0FkqXZMXTD6HUVg7gSwsUykrOLYGTI3kUsTYH/h160XsDPIIuremKwPyacGYPw==</P><Q>2M7Nm6HMqehbXydsdqfqUOBJbodCalA1NJKam20enzKS9ViBW+iDp2AqOLdRbRWnNJ+DclReSiqJ3i04pSSE4w==</Q><DP>l3tV/9M+ZcMgrjuRDAsLeQo/ZeLc7rjB45v7nANo+fL8RdL+P+atyFiCg/LmbNJGawOzeOwJRPi+1tc28GZTnQ==</DP><DQ>dCZmlGgcIzJuMQ4H15GhwG5IKMSh2h7aQi4AUiAldZg8juA5fSmVzoeAVx4uq0gcad7DJu74DLe5YKPNYvurjQ==</DQ><InverseQ>GdOR3nD8dkNmu4HH+qEZWMvsnCp1x/jgO3YuBX+/CMrz2bBseaNftR3GlrKMWdwqd7rjzlK3/111Np8qOvyldA==</InverseQ><D>SC/pcmt7X8bonDcjDnQkEXVYZFIPXe1E6HDQa3cLCWMV/UOXicZiS8JNhXsdw1E7fxAUUDJvSVMWJP6RMsVozqZFxKds/dqyboF1EquUB2/cc3APDeT770vH3LQUqnkQ4zw/E5MY1b3uOTybONDYL4N7aS79JPkJzxUtIDnALJ0=</D></RSAKeyValue>";

    private static String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKTWwzIpYT6rrvWb0fxiJPZ0qqtevMCkq_7-1_nGMxhoRQEPWtDwzUobfOO4Um2jOXAvIos6tSdNTmbAq8L1q1qlZy4SzpXc0QKe08fGnb4A0UP15COFOFzYmrgmEOxTiQ3Z-FgmK1r98lVPxuM02NpKFxm9c8qA6YzPzqFDKrRdAgMBAAECgYBlqsQnmxgMVE2L1LhV5p6Pl3NwUPUwCCDBHKNOvazj_-OXfMXxmqOzcnK77QKB9qtFgvzWTLZQcEDL5UfK7kTpmvjnOPX57Kqg53T5if3vpQEnpA2bzARB6gF6b0o3v6WkdmmtCIxNWCZBHoEZNVtFRzf0GbNyuK8EGK1GJjHT4QJBAOqb6nqkp71fqmzb8z6TS-tyiNiVN-LuAdaSkOHH2CIRnPFx9J2L0KcWqqPLqi9Vr_FHzntqo-AmPAkBn_436SUCQQCz3lHgfuFH9CF33rSzQ0ookVLqwqNMATF0Be8Vy2s21bMQ58ySG8HEDcL_7AzEgQBxQuT6B0yDYL6rFw_To4TZAkEAg2oOr-AnbXZ22j9iQ3zym9IuQVNkWKMFOJIkwbPVp0_CX6R7Zvx4Rf_2WYWtsJII2dhlGtCjNt1z53-XhSkAWQJAI1aup3jGMijY2oA66G10CHxk2hLbmncxjCYOTy3kJqXdxJiGLL6fVzGM-lMaPxFJUPdfnXAJrFY5n8usJFygIQJAfkn6_IpOnPg5onvvUfxIdQX4ewuHEcRH_id7LOJpRdJ3a4VRVTLmKO2x9LT2tZz7YpnXVSo82wUlQdR-tMKUOA";

    private static String xmlPublicKey = "";

    private static String instrumentPrivateKey = "";

    private static String instrumentPublicKey = "";


    public static String RsaDecryptCode(String encryptedInput) throws InvalidKeySpecException, NoSuchAlgorithmException {
        String targetMap = RSAUtils.privateDecrypt(encryptedInput, RSAUtils.getPrivateKey(privateKey));
        return targetMap;
    }


    public static String RsaDecrypt(String encryptedInput) {
        if (encryptedInput == null || encryptedInput.isEmpty()) {
            return "";
        }

        try {
            String pemPrivateKey = RSAXmlToPem.transXmlStrToPem(xmlPrivateKey);
            System.out.println("pemPrivateKey = " + pemPrivateKey);

            String content = RSAUtils.privateDecrypt(encryptedInput, RSAUtils.getPrivateKey(pemPrivateKey));

            return content;
        } catch (Exception ex) {
            return encryptedInput;// string.Empty;
        }
    }

    public static String InstrumentRSAEncrypt(String rawInput) {
        if (rawInput == null || rawInput.isEmpty()) {
            return "";
        }

        try {
            String pemPublicKey = RSAXmlToPem.transXmlStrToPem(instrumentPublicKey);

            //String content = RSAUtils.publicEncrypt(rawInput, RSAUtils.getPublicKey(pemPublicKey));

            String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCk1sMyKWE-q671m9H8YiT2dKqrXrzApKv-_tf5xjMYaEUBD1rQ8M1KG3zjuFJtozlwLyKLOrUnTU5mwKvC9atapWcuEs6V3NECntPHxp2-ANFD9eQjhThc2Jq4JhDsU4kN2fhYJita_fJVT8bjNNjaShcZvXPKgOmMz86hQyq0XQIDAQAB";
            String privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKTWwzIpYT6rrvWb0fxiJPZ0qqtevMCkq_7-1_nGMxhoRQEPWtDwzUobfOO4Um2jOXAvIos6tSdNTmbAq8L1q1qlZy4SzpXc0QKe08fGnb4A0UP15COFOFzYmrgmEOxTiQ3Z-FgmK1r98lVPxuM02NpKFxm9c8qA6YzPzqFDKrRdAgMBAAECgYBlqsQnmxgMVE2L1LhV5p6Pl3NwUPUwCCDBHKNOvazj_-OXfMXxmqOzcnK77QKB9qtFgvzWTLZQcEDL5UfK7kTpmvjnOPX57Kqg53T5if3vpQEnpA2bzARB6gF6b0o3v6WkdmmtCIxNWCZBHoEZNVtFRzf0GbNyuK8EGK1GJjHT4QJBAOqb6nqkp71fqmzb8z6TS-tyiNiVN-LuAdaSkOHH2CIRnPFx9J2L0KcWqqPLqi9Vr_FHzntqo-AmPAkBn_436SUCQQCz3lHgfuFH9CF33rSzQ0ookVLqwqNMATF0Be8Vy2s21bMQ58ySG8HEDcL_7AzEgQBxQuT6B0yDYL6rFw_To4TZAkEAg2oOr-AnbXZ22j9iQ3zym9IuQVNkWKMFOJIkwbPVp0_CX6R7Zvx4Rf_2WYWtsJII2dhlGtCjNt1z53-XhSkAWQJAI1aup3jGMijY2oA66G10CHxk2hLbmncxjCYOTy3kJqXdxJiGLL6fVzGM-lMaPxFJUPdfnXAJrFY5n8usJFygIQJAfkn6_IpOnPg5onvvUfxIdQX4ewuHEcRH_id7LOJpRdJ3a4VRVTLmKO2x9LT2tZz7YpnXVSo82wUlQdR-tMKUOA";
            //String content = RSAUtils.publicEncrypt(rawInput, RSAUtils.getPublicKey(privateKey));

            String content = RSAUtils.publicEncrypt(rawInput, RSAUtils.getPublicKey(publicKey));  //传入明文和公钥加密,得到密文

            return content;
        } catch (Exception ex) {
            return "";
        }
    }

    public static void main(String[] args) throws Exception {

        String Sn = "-958917661";
        calcCncryptionTxt(Sn);


//        String str1 = "PgLZL3xYnngxRJ2AIEdpR7AOUIbxOytjWIys9WuXKK5gOsYs7qjJ9qas693X7bcJWoOHrefcnqO2EzEtEfo4ThJYr+g1xILCShKbtdZ7U2eaItEyTVIb94UBEL70mvibFCoT+45/n7sGhqKss8x4pzwjlGqhqvK7Bw+Gairc2zY=";
//
//        try {
//            String pemPrivateKey = RSAXmlToPem.transXmlStrToPem(xmlPrivateKey);
//
//            String str2 = RSAUtils.privateDecrypt(str1, RSAUtils.getPrivateKey(pemPrivateKey));
//
//            System.out.println(str2);
//        } catch (Exception ex) {
//            ex.printStackTrace();
//        }
    }

    /**
     * 生成密文演示
     *
     * @param Sn
     * @throws Exception
     */
    public static void calcCncryptionTxt(String Sn) throws Exception {

        // 生成新的密钥
        Map<String, String> keyMap = RSAUtils.createKeys(1024);
        String publicKey = keyMap.get("publicKey");
        String privateKey = keyMap.get("privateKey");


        System.out.println("publicKey =" + publicKey);
        System.out.println("privateKey =" + privateKey);
        System.out.println("------------------------------------------");

        // 用已生成的密钥
        publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYJOoKOULvUaIbG6EAcV5cMmk95tKiFzmX7LE0StFrAGTitz_TVCto0sUgaNQBQnw1nQCFyOrgCoSymrc4Y15meQvrJTBkAAIZTAE9-rx6sMDUyGXkq8FaJLeq9XD1OXMhfIo0nmCzRkFvAvq-Nkxlt92q7SkYXTfeTbEvAB1bbQIDAQAB";
        privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJgk6go5Qu9RohsboQBxXlwyaT3m0qIXOZfssTRK0WsAZOK3P9NUK2jSxSBo1AFCfDWdAIXI6uAKhLKatzhjXmZ5C-slMGQAAhlMAT36vHqwwNTIZeSrwVokt6r1cPU5cyF8ijSeYLNGQW8C-r42TGW33artKRhdN95NsS8AHVttAgMBAAECgYA8NBc8Vm5LHuVjjqpF75ZkKzqD2SuuIjyTBoEUbSqzCaPiJpsl9595yjVg3rpNiU53NJo5VkjCwMbDmf84W_HFQNJLNI9X-NnQd1e3G-MU8KYJ5xKTUANBOD4MqTwzK_I64dmJRtHpOuInP7tfmtRolH7Tv6cuzHcw1wzeUd2TYQJBAPXv4mXOOKhS2vDcQoON9lL8aJCa4fruKkRfqrauJd_rf91lU3jrGt789ErLH_mKPECHb5C7XXJLka3dZTvPHpkCQQCeXpQMDPtp_RP0vBoe6vy5MDSH_C_eAGqWcT5auoOI0hWmzN98AHLal_E_snPvgNPmcAYg8W62YvPs-gbsiIv1AkBsq0hgL5T9W07-qnSBaJL07C7P5n__vojSmWr3OU_m90oSa5lMjqXa7e6LteE1BA3HfZCgZQnPXPDL4E3A9a3RAj9EDF-QzgM5h4ULo8eVgWMWL6BizfnDampiOkaQtZPPNnYXNfDRV2ACjUccF5G5V6G1Ym0_un3X4KzvJn_TItkCQQCgEy9H312LOJ_2iy4yVybuTxhR-wlgdb-u-W4qIdfF72JLplCXH9I-yyIgHtpMJ1OvrsGmwzun7O-nyN2D_ll_";


        LicenseHelper license = new LicenseHelper();
        //String Sn = license.GetSn();

        System.out.println("Sn = " + Sn);

        Date outDateEn = DateUtil.calcDate(new Date(), Calendar.MONTH, 12);

        Map map = new HashMap();
        map.put("product", "Catch");
        map.put("hardware", String.valueOf(Sn));
        map.put("outDate", String.valueOf(outDateEn.getTime()));
        System.out.println("test_date = " + map);
        String encodedData = RSAUtils.publicEncrypt(JSON.toJSONString(map), RSAUtils.getPublicKey(publicKey));  //传入明文和公钥加密,得到密文

        System.out.println();
        System.out.println("加密字典 = " + encodedData + "\n");


        String decodedData = RSAUtils.privateDecrypt(encodedData, RSAUtils.getPrivateKey(privateKey)); //传入密文和私钥,得到明文
        System.out.println("解密后文字: \r\n" + decodedData);

        Map<String, String> target = JSON.parseObject(decodedData, HashMap.class);

        System.out.println(target);
        if (target.containsKey("outDate")) {
            Date outDate = new Date(Long.valueOf(target.get("outDate")));
            if (DateUtil.compartDateTime(new Date(), outDate) > 0) {
                System.out.println("已过期");
            } else {
                System.out.println("未过期");
            }
            System.out.println("outDate = " + DateUtil.formatDateTimeString(outDate));
        }


//        Map<String, String> keyMap = RSAUtils.createKeys(1024);
//        String publicKey = keyMap.get("publicKey");
//        String privateKey = keyMap.get("privateKey");
//        System.out.println("公钥: \n\r" + publicKey);
//        System.out.println("私钥： \n\r" + privateKey);
//
//        System.out.println("公钥加密——私钥解密");
//        String str = "站在大明门前守卫的禁卫军，事先没有接到\n" + "有关的命令，但看到大批盛装的官员来临，也就\n" + "以为确系举行大典，因而未加询问。进大明门即\n" + "为皇城。文武百官看到端门午门之前气氛平静，\n" + "城楼上下也无朝会的迹象，既无几案，站队点名\n" + "的御史和御前侍卫“大汉将军”也不见踪影，不免\n"
//                + "心中揣测，互相询问：所谓午朝是否讹传？";
//        System.out.println("\r明文：\r\n" + str);
//        System.out.println("\r明文大小：\r\n" + str.getBytes().length);
//        String encodedData = RSAUtils.publicEncrypt(str, RSAUtils.getPublicKey(publicKey));  //传入明文和公钥加密,得到密文
//        System.out.println("密文：\r\n" + encodedData);
//        String decodedData = RSAUtils.privateDecrypt(encodedData, RSAUtils.getPrivateKey(privateKey)); //传入密文和私钥,得到明文
//        System.out.println("解密后文字: \r\n" + decodedData);

    }
}
